package gossipServices.pss;

import gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.view.ConcretePartialView;
import gossipServices.basic.view.PartialView;
import gossipServices.basic.view.PartialView.PEER_SELECTION_ENUM;
import gossipServices.gossipMessages.PssMessage;
import gossipServices.gossipThread.ActiveGossipThread;
import gossipServices.network.NetworkServicesAPI;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

import messages.Message;

/**
 * 
 * Description:
 * This class extends the ActiveGossipThread and contains 
 * all actions performed by the active thread of the 
 * Peer Sampling Service.
 *
 */
public class ActivePSSThread extends ActiveGossipThread{
	
	protected NetworkServicesAPI nia;
	protected boolean push = true, pull;
	protected int H, S;
	
	private Logger log = Logger.getLogger("ActivePSS_log");;

	/**
	 * @param name
	 * @param cycleLenghtMsec
	 * @param view
	 * @param pull2 
	 * @param push2 
	 * @param s2 
	 * @param h2 
	 * @param c2 
	 * @param gossipProtocol
	 */
	public ActivePSSThread(String name, long cycleLenghtMsec, PartialView view, 
			NetworkServicesAPI net, int h2, int s2, boolean push2, boolean pull2) {
		super(name, cycleLenghtMsec, view);		
		nia = net;
		H = h2;
		S = s2;
		push = push2;
		pull = pull2;
		log.setLevel(Level.OFF);
	}

	@Override
	public void activeThreadInstructions() throws InterruptedException {
		NodeDescriptor p;		
		log.info("starting active thread");
		synchronized (view) {
			p = view.selectPeer(PEER_SELECTION_ENUM.rand);			
			log.info("selected peer "+p);
		}
		PartialView buffer = new ConcretePartialView(0);
		NodeDescriptor myDescriptor = null;
		
		try {
			myDescriptor = new ConcreteNodeDescriptor(nia.getNodeName(),
					InetAddress.getLocalHost(),new Integer(0));
			log.info("myDescriptor is: "+myDescriptor);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
			
		if(push){
			buffer.insertNodeDescriptor(myDescriptor);				
			synchronized (view) {
				log.info("view permutation");
				view.permute(H);
				//? move the H oldest elements at the end of the view
				buffer.append(view.head(view.size()/2-1));					
			}
			Message m = new PssMessage(buffer,myDescriptor,p, this.getClass());
			log.info("sending message m..."+m+" to "+p);
			nia.send(m, p);					

		}else{
			//empty view to trigger response
			Message m = new PssMessage(buffer,myDescriptor, p, this.getClass());
			log.info("sending an empty view...");
			nia.send(m, p);
		}
		
		if(pull){
			log.info("pull phase:");
			PartialView bufferp = new ConcretePartialView(0);
			log.info("waiting for incoming messages");
			Message m = nia.receive(p);
			if(m != null){
				log.info(Thread.currentThread().getName()+"received "+m+" from "+p);
				bufferp = (PartialView) m.getBody();
				synchronized (view) {
					view.select(view.size(), H, S, bufferp, myDescriptor);
					log.info("select on the view");
					view.notify();				
				}
			}
		}
		synchronized (view) {
			view.increaseAge();
//			System.out.print(nia.getNodeName()+" view:\n"+view+"\n");
		}
	}


}
